package edu.osu.cse.c651.cryptochess;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Random;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.text.format.Formatter;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.PopupWindow;
import android.widget.TextView;
/**
 * This class controls how two clients will connect to each other. It shows
 * each client the accounts that are online, and dynamically updates the
 * list as clients join and leave the find game activity.
 * 
 * @author Katherine Tornwall
 */
public class FindGameActivity extends Activity {
	/**
	 * A reference to the controller that receives the messages for the find
	 * game client.
	 */
	private static FindGameController ctrl;
	/**
	 * The local user of this client.
	 */
	private User user;
	/**
	 * The popup that will show when another user has requested to play a game
	 * with this user.
	 */
	private PopupWindow gameRequestPopup;
	/**
	 * The popup that will show while a request made by this user is still
	 * pending.
	 */
	private PopupWindow pendingRequest;
	/**
	 * The popup that will show when a request was rejected.
	 */
	private PopupWindow rejectRequest;
	/**
	 * The popup that will show when a request was accepted.
	 */
	private PopupWindow acceptRequest;
	/**
	 * The handler used to time the request windows.
	 */
	private Handler h = new Handler();
	/**
	 * The runnable used to close the pending request window after a certain
	 * amount of time.
	 */
	private Runnable pendReqTimeout = new Runnable() {
		@Override
		public void run() {
			if (pendingRequest != null) {
				pendingRequest.dismiss();
			}
		}
	};
	/**
	 * The runnable used to close the rejected request window after a certain
	 * amount of time.
	 */
	private Runnable rejectTimeout = new Runnable() {
		@Override
		public void run() {
			if (pendingRequest != null) {
				pendingRequest.dismiss();
			}
		}
	};
	/**
	 * The runnable used to close the accepted request window after a certain
	 * amount of time.
	 */
	private Runnable acceptTimeout = new Runnable() {
		@Override
		public void run() {
			if (pendingRequest != null) {
				pendingRequest.dismiss();
			}
		}
	};

	/**
	 * Sets up the interface for the find game class. It starts the find game
	 * control so it is now listening for other users. It also announces over a
	 * broadcast that this controller is now in the find game activity.<br />
	 * <br />
	 * {@inheritDoc}
	 * 
	 * @author Katherine Tornwall
	 */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.find_game);

		ListView userList = (ListView) findViewById(R.id.userList);
		ArrayList<User> userArray = new ArrayList<User>();
		UserAdapter userAdapter = new UserAdapter(this, R.layout.user_layout,
				userArray);
		userList.setAdapter(userAdapter);

		if (ctrl == null)
			ctrl = FindGameController.getInstance();

		FindGameController.setActivity(this);

		Intent intent = this.getIntent();
		user = (User) intent.getSerializableExtra("user");
		ctrl.setUser(user);

		WifiManager wifimanager = (WifiManager) getSystemService(WIFI_SERVICE);
		String s = Formatter.formatIpAddress(wifimanager.getConnectionInfo()
				.getIpAddress());
		ctrl.setAddress(s);
		try {
			user.setIpAddress(InetAddress.getByName(s));
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}

		userList.setOnItemClickListener(new ListView.OnItemClickListener() {
			public void onItemClick(AdapterView<?> parent, View view, int pos,
					long id) {
				final User u = (User) parent.getItemAtPosition(pos);
				SendPlayRequest req = new SendPlayRequest();
				req.execute(u, user);
				requestPendingNotification();
			}
		});
	}

	/**
	 * Adds a user to the list of active users in the find game activity.
	 * @param u the user to add to the list
	 * @author Katherine Tornwall
	 */
	public void addUser(User u) {
		ListView userList = (ListView) findViewById(R.id.userList);
		UserAdapter userAdapter = (UserAdapter) userList.getAdapter();
		userAdapter.add(u);
		userAdapter.notifyDataSetChanged();
	}

	/**
	 * Removes a user from the list of active users in the find game activity.
	 * @param u the user to remove from the list
	 * @author Katherine Tornwall
	 */
	public void removeUser(User u) {
		ListView userList = (ListView) findViewById(R.id.userList);
		UserAdapter userAdapter = (UserAdapter) userList.getAdapter();
		userAdapter.remove(u);
		userAdapter.notifyDataSetChanged();
	}

	/**
	 * Shows a request from another user who wants to play a game from this client.
	 * This gives the user a popup that blocks touch activities except in the
	 * client. The client will have an option of selecting to accept or deny the
	 * request.
	 * 
	 * @param u the user who requested a game
	 * @author Katherine Tornwall
	 */
	public void gameRequestNotification(User u) {
		View layout = null;
		if (gameRequestPopup == null) {
			LayoutInflater inflater = getLayoutInflater();

			// Inflate the view from a predefined XML layout
			layout = inflater.inflate(R.layout.game_request_dialog, null);
			gameRequestPopup = new PopupWindow(layout,
					LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT, true);

			final User opponent = u;

			Button yes = (Button) layout.findViewById(R.id.playYes);
			Button no = (Button) layout.findViewById(R.id.playNo);

			yes.setOnClickListener(new Button.OnClickListener() {
				@Override
				public void onClick(View view) {
					// Generate the color for each player
					Random rng = new Random();

					if(rng.nextBoolean()) {
						user.setColor(Color.WHITE);
						opponent.setColor(Color.BLACK);
					} else {
						user.setColor(Color.BLACK);
						opponent.setColor(Color.WHITE);
					}
					
					ctrl.setUser(user);

					SendAcceptRequest req = new SendAcceptRequest();
					req.execute(opponent, user);
					gameRequestPopup.dismiss();
					Intent intent = new Intent(FindGameActivity.this,
							GameActivity.class);
					intent.putExtra("user", user);
					intent.putExtra("opponent", opponent);
					intent.putExtra("color", user.getColor());
					intent.putExtra("ip", opponent.getIpAddress());
					FindGameActivity.this.startActivity(intent);
				}
			});

			no.setOnClickListener(new Button.OnClickListener() {
				@Override
				public void onClick(View view) {
					gameRequestPopup.dismiss();
				}
			});
		} else {
			layout = gameRequestPopup.getContentView();
		}

		// Set the opponent's information to show in the popup
		TextView username = (TextView) layout.findViewById(R.id.textUsername);
		TextView rating = (TextView) layout.findViewById(R.id.textRating);
		TextView winLoss = (TextView) layout.findViewById(R.id.textWinLoss);
		ImageView userIcon = (ImageView) layout.findViewById(R.id.imageUserIcon);

		username.setText("" + u.getUsername());
		rating.setText("" + u.getRating());
		winLoss.setText("" + u.getWins() + " W - " + u.getLosses()
				+ " L - " + u.getDraws() + " D");
		userIcon.setImageResource(u.getUserIcon().getIconId());

		// display the popup in the center
		gameRequestPopup.showAtLocation(layout, Gravity.CENTER, 0, 0);
	}

	/**
	 * Shows the user the dialog that says their request is pending.
	 * 
	 * @author Katherine Tornwall
	 */
	public void requestPendingNotification() {
		View layout = null;
		if (pendingRequest == null) {
			// Inflate the view from a predefined XML layout
			LayoutInflater inflater = getLayoutInflater();
			layout = inflater.inflate(R.layout.pending_request, null);
			pendingRequest = new PopupWindow(layout, LayoutParams.FILL_PARENT,
					LayoutParams.FILL_PARENT, true);
		} else {
			layout = pendingRequest.getContentView();
		}
		// display the popup in the center
		pendingRequest.showAtLocation(layout, Gravity.CENTER, 0, 0);
		h.postDelayed(pendReqTimeout, 30000);
	}

	/**
	 * Dismisses the pending request popup.
	 * 
	 * @author Katherine Tornwall
	 */
	public void dismissPendingNotification() {
		pendingRequest.dismiss();
	}

	/**
	 * Shows a popup to the user that their game request was rejected by the other
	 * client.
	 * 
	 * @author Katherine Tornwall
	 */
	public void requestRejectedNotification() {
		View layout = null;
		if (rejectRequest == null) {
			// Inflate the view from a predefined XML layout
			LayoutInflater inflater = getLayoutInflater();
			layout = inflater.inflate(R.layout.request_rejected, null);
			rejectRequest = new PopupWindow(layout, LayoutParams.FILL_PARENT,
					LayoutParams.FILL_PARENT, true);
		} else {
			layout = rejectRequest.getContentView();
		}
		// display the popup in the center
		rejectRequest.showAtLocation(layout, Gravity.CENTER, 0, 0);
		h.postDelayed(rejectTimeout, 2500);
	}

	/**
	 * Shows a popup to the user that their game request was accepted by the other
	 * client.
	 * 
	 * @author Katherine Tornwall
	 */
	public void requestAcceptedNotification() {
		View layout = null;
		if (acceptRequest == null) {
			// Inflate the view from a predefined XML layout
			LayoutInflater inflater = getLayoutInflater();
			layout = inflater.inflate(R.layout.request_accepted, null);
			acceptRequest = new PopupWindow(layout, LayoutParams.FILL_PARENT,
					LayoutParams.FILL_PARENT, true);
		} else {
			layout = acceptRequest.getContentView();
		}
		// display the popup in the center
		rejectRequest.showAtLocation(layout, Gravity.CENTER, 0, 0);
		h.postDelayed(acceptTimeout, 2500);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void onResume() {
		super.onResume();
		ctrl.broadcastAvailable();
		if (ctrl.getStatus() != AsyncTask.Status.RUNNING)
			ctrl.execute(this);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void onPause() {
		if(pendingRequest != null)
			pendingRequest.dismiss();
		ctrl.cancel(true);
		ctrl.broadcastRemove();
		super.onPause();
		finish();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void onDestroy() {
		ListView userList = (ListView) findViewById(R.id.userList);
		UserAdapter u = (UserAdapter) userList.getAdapter();
		u.clear();
		super.onDestroy();
	}

	/**
	 * Send an opposing user a play request. User 0 is the opponent you want to
	 * play and User 1 is the current user.
	 */
	private class SendPlayRequest extends AsyncTask<User, Void, Void> {
		final static private String CRYPTO_CHESS_UUID = "{58e9b2b7-b74d-4ccb-ba76-89f757a32472}";
		final static private int LISTEN_PORT = 19725;

		protected Void doInBackground(User... user) {
			try {
				InetAddress addr = user[0].getIpAddress();
				byte[] playBuf = (CRYPTO_CHESS_UUID + ";"
						+ Request.play.ordinal() + ";" + user[1].toString() + ";")
						.getBytes();
				DatagramPacket playPacket = new DatagramPacket(playBuf, 0,
						playBuf.length, addr, LISTEN_PORT);

				DatagramSocket socket = new DatagramSocket();
				socket.send(playPacket);
			} catch (Exception e) {
				e.printStackTrace();
			}
			return null;
		}
	}

	/**
	 * Send an opposing user a accept request. User 0 is the opponent you want
	 * to play and User 1 is the current user.
	 */
	private class SendAcceptRequest extends AsyncTask<User, Void, Void> {
		final static private String CRYPTO_CHESS_UUID = "{58e9b2b7-b74d-4ccb-ba76-89f757a32472}";
		final static private int LISTEN_PORT = 19725;

		protected Void doInBackground(User... user) {
			try {
				InetAddress addr = user[0].getIpAddress();
				byte[] acceptBuf = (CRYPTO_CHESS_UUID + ";"
						+ Request.accept.ordinal() + ";" + user[1].toString() + ";" + user[0].getColor().ordinal() + ";")
						.getBytes();
				DatagramPacket acceptPacket = new DatagramPacket(acceptBuf, 0,
						acceptBuf.length, addr, LISTEN_PORT);

				DatagramSocket socket = new DatagramSocket();
				socket.send(acceptPacket);
				ctrl.cancel(true);
			} catch (Exception e) {
				e.printStackTrace();
			}
			return null;
		}
	}

	/**
	 * The adapter for showing the users in the list view.
	 * 
	 * @author Katherine Tornwall
	 */
	private class UserAdapter extends ArrayAdapter<User> {

		private ArrayList<User> items;

		public UserAdapter(Context context, int textViewResourceId,
				ArrayList<User> items) {
			super(context, textViewResourceId, items);
			this.items = items;
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			View v = convertView;
			if (v == null) {
				LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
				v = vi.inflate(R.layout.user_layout, null);
			}
			User u = items.get(position);
			if (u != null) {
				TextView username = (TextView) v
						.findViewById(R.id.textUsername);
				TextView rating = (TextView) v.findViewById(R.id.textRating);
				TextView winLoss = (TextView) v.findViewById(R.id.textWinLoss);
				ImageView userIcon = (ImageView) v.findViewById(R.id.imageUserIcon);

				username.setText("" + u.getUsername());
				rating.setText("" + u.getRating());
				winLoss.setText("" + u.getWins() + " W - " + u.getLosses()
						+ " L - " + u.getDraws() + " D");
				userIcon.setImageResource(u.getUserIcon().getIconId());
			}
			return v;
		}
	}
}
